﻿using System.Collections.Generic;
using System.Linq;

namespace System.Net
{
    /// <summary>
    /// xHTML document processing Utility.
    /// </summary>
    public static class XhtmlUtility
    {
        #region Replacements Maps

        private static readonly KeyValuePair<char, string>[] SpecialReservedSymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 38, "&amp;"),
                      new KeyValuePair<char, string>((char) 60, "&lt;"),
                      new KeyValuePair<char, string>((char) 62, "&gt;")
                  };

        private static readonly KeyValuePair<char, string>[] ReservedSymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 34, "&quot;"),
                      new KeyValuePair<char, string>((char) 39, "&apos;")
                  };

        private static readonly KeyValuePair<char, string>[] Iso88591SymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 160, "&nbsp;"),
                      new KeyValuePair<char, string>((char) 161, "&iexcl;"),
                      new KeyValuePair<char, string>((char) 162, "&cent;"),
                      new KeyValuePair<char, string>((char) 163, "&pound;"),
                      new KeyValuePair<char, string>((char) 164, "&curren;"),
                      new KeyValuePair<char, string>((char) 165, "&yen;"),
                      new KeyValuePair<char, string>((char) 166, "&brvbar;"),
                      new KeyValuePair<char, string>((char) 167, "&sect;"),
                      new KeyValuePair<char, string>((char) 168, "&uml;"),
                      new KeyValuePair<char, string>((char) 169, "&copy;"),
                      new KeyValuePair<char, string>((char) 170, "&ordf;"),
                      new KeyValuePair<char, string>((char) 171, "&laquo;"),
                      new KeyValuePair<char, string>((char) 172, "&not;"),
                      new KeyValuePair<char, string>((char) 173, "&shy;"),
                      new KeyValuePair<char, string>((char) 174, "&reg;"),
                      new KeyValuePair<char, string>((char) 175, "&macr;"),
                      new KeyValuePair<char, string>((char) 176, "&deg;"),
                      new KeyValuePair<char, string>((char) 177, "&plusmn;"),
                      new KeyValuePair<char, string>((char) 178, "&sup2;"),
                      new KeyValuePair<char, string>((char) 179, "&sup3;"),
                      new KeyValuePair<char, string>((char) 180, "&acute;"),
                      new KeyValuePair<char, string>((char) 181, "&micro;"),
                      new KeyValuePair<char, string>((char) 182, "&para;"),
                      new KeyValuePair<char, string>((char) 183, "&middot;"),
                      new KeyValuePair<char, string>((char) 184, "&cedil;"),
                      new KeyValuePair<char, string>((char) 185, "&sup1;"),
                      new KeyValuePair<char, string>((char) 186, "&ordm;"),
                      new KeyValuePair<char, string>((char) 187, "&raquo;"),
                      new KeyValuePair<char, string>((char) 188, "&frac14;"),
                      new KeyValuePair<char, string>((char) 189, "&frac12;"),
                      new KeyValuePair<char, string>((char) 190, "&frac34;"),
                      new KeyValuePair<char, string>((char) 191, "&iquest;"),
                      new KeyValuePair<char, string>((char) 215, "&times;"),
                      new KeyValuePair<char, string>((char) 247, "&divide;")
                  };

        private static readonly KeyValuePair<char, string>[] Iso88591CharactersReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 192, "&Agrave;"),
                      new KeyValuePair<char, string>((char) 193, "&Aacute;"),
                      new KeyValuePair<char, string>((char) 194, "&Acirc;"),
                      new KeyValuePair<char, string>((char) 195, "&Atilde;"),
                      new KeyValuePair<char, string>((char) 196, "&Auml;"),
                      new KeyValuePair<char, string>((char) 197, "&Aring;"),
                      new KeyValuePair<char, string>((char) 198, "&AElig;"),
                      new KeyValuePair<char, string>((char) 199, "&Ccedil;"),
                      new KeyValuePair<char, string>((char) 200, "&Egrave;"),
                      new KeyValuePair<char, string>((char) 201, "&Eacute;"),
                      new KeyValuePair<char, string>((char) 202, "&Ecirc;"),
                      new KeyValuePair<char, string>((char) 203, "&Euml;"),
                      new KeyValuePair<char, string>((char) 204, "&Igrave;"),
                      new KeyValuePair<char, string>((char) 205, "&Iacute;"),
                      new KeyValuePair<char, string>((char) 206, "&Icirc;"),
                      new KeyValuePair<char, string>((char) 207, "&Iuml;"),
                      new KeyValuePair<char, string>((char) 208, "&ETH;"),
                      new KeyValuePair<char, string>((char) 209, "&Ntilde;"),
                      new KeyValuePair<char, string>((char) 210, "&Ograve;"),
                      new KeyValuePair<char, string>((char) 211, "&Oacute;"),
                      new KeyValuePair<char, string>((char) 212, "&Ocirc;"),
                      new KeyValuePair<char, string>((char) 213, "&Otilde;"),
                      new KeyValuePair<char, string>((char) 214, "&Ouml;"),
                      new KeyValuePair<char, string>((char) 216, "&Oslash;"),
                      new KeyValuePair<char, string>((char) 217, "&Ugrave;"),
                      new KeyValuePair<char, string>((char) 218, "&Uacute;"),
                      new KeyValuePair<char, string>((char) 219, "&Ucirc;"),
                      new KeyValuePair<char, string>((char) 220, "&Uuml;"),
                      new KeyValuePair<char, string>((char) 221, "&Yacute;"),
                      new KeyValuePair<char, string>((char) 222, "&THORN;"),
                      new KeyValuePair<char, string>((char) 223, "&szlig;"),
                      new KeyValuePair<char, string>((char) 224, "&agrave;"),
                      new KeyValuePair<char, string>((char) 225, "&aacute;"),
                      new KeyValuePair<char, string>((char) 226, "&acirc;"),
                      new KeyValuePair<char, string>((char) 227, "&atilde;"),
                      new KeyValuePair<char, string>((char) 228, "&auml;"),
                      new KeyValuePair<char, string>((char) 229, "&aring;"),
                      new KeyValuePair<char, string>((char) 230, "&aelig;"),
                      new KeyValuePair<char, string>((char) 231, "&ccedil;"),
                      new KeyValuePair<char, string>((char) 232, "&egrave;"),
                      new KeyValuePair<char, string>((char) 233, "&eacute;"),
                      new KeyValuePair<char, string>((char) 234, "&ecirc;"),
                      new KeyValuePair<char, string>((char) 235, "&euml;"),
                      new KeyValuePair<char, string>((char) 236, "&igrave;"),
                      new KeyValuePair<char, string>((char) 237, "&iacute;"),
                      new KeyValuePair<char, string>((char) 238, "&icirc;"),
                      new KeyValuePair<char, string>((char) 239, "&iuml;"),
                      new KeyValuePair<char, string>((char) 240, "&eth;"),
                      new KeyValuePair<char, string>((char) 241, "&ntilde;"),
                      new KeyValuePair<char, string>((char) 242, "&ograve;"),
                      new KeyValuePair<char, string>((char) 243, "&oacute;"),
                      new KeyValuePair<char, string>((char) 244, "&ocirc;"),
                      new KeyValuePair<char, string>((char) 245, "&otilde;"),
                      new KeyValuePair<char, string>((char) 246, "&ouml;"),
                      new KeyValuePair<char, string>((char) 248, "&oslash;"),
                      new KeyValuePair<char, string>((char) 249, "&ugrave;"),
                      new KeyValuePair<char, string>((char) 250, "&uacute;"),
                      new KeyValuePair<char, string>((char) 251, "&ucirc;"),
                      new KeyValuePair<char, string>((char) 252, "&uuml;"),
                      new KeyValuePair<char, string>((char) 253, "&yacute;"),
                      new KeyValuePair<char, string>((char) 254, "&thorn;"),
                      new KeyValuePair<char, string>((char) 255, "&yuml;")
                  };

        private static readonly KeyValuePair<char, string>[] MathSymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 8704, "&forall;"),
                      new KeyValuePair<char, string>((char) 8706, "&part;"),
                      new KeyValuePair<char, string>((char) 8707, "&exist;"),
                      new KeyValuePair<char, string>((char) 8709, "&empty;"),
                      new KeyValuePair<char, string>((char) 8711, "&nabla;"),
                      new KeyValuePair<char, string>((char) 8712, "&isin;"),
                      new KeyValuePair<char, string>((char) 8713, "&notin;"),
                      new KeyValuePair<char, string>((char) 8715, "&ni;"),
                      new KeyValuePair<char, string>((char) 8719, "&prod;"),
                      new KeyValuePair<char, string>((char) 8721, "&sum;"),
                      new KeyValuePair<char, string>((char) 8722, "&minus;"),
                      new KeyValuePair<char, string>((char) 8727, "&lowast;"),
                      new KeyValuePair<char, string>((char) 8730, "&radic;"),
                      new KeyValuePair<char, string>((char) 8733, "&prop;"),
                      new KeyValuePair<char, string>((char) 8734, "&infin;"),
                      new KeyValuePair<char, string>((char) 8736, "&ang;"),
                      new KeyValuePair<char, string>((char) 8743, "&and;"),
                      new KeyValuePair<char, string>((char) 8744, "&or;"),
                      new KeyValuePair<char, string>((char) 8745, "&cap;"),
                      new KeyValuePair<char, string>((char) 8746, "&cup;"),
                      new KeyValuePair<char, string>((char) 8747, "&int;"),
                      new KeyValuePair<char, string>((char) 8756, "&there4;"),
                      new KeyValuePair<char, string>((char) 8764, "&sim;"),
                      new KeyValuePair<char, string>((char) 8773, "&cong;"),
                      new KeyValuePair<char, string>((char) 8776, "&asymp;"),
                      new KeyValuePair<char, string>((char) 8800, "&ne;"),
                      new KeyValuePair<char, string>((char) 8801, "&equiv;"),
                      new KeyValuePair<char, string>((char) 8804, "&le;"),
                      new KeyValuePair<char, string>((char) 8805, "&ge;"),
                      new KeyValuePair<char, string>((char) 8834, "&sub;"),
                      new KeyValuePair<char, string>((char) 8835, "&sup;"),
                      new KeyValuePair<char, string>((char) 8836, "&nsub;"),
                      new KeyValuePair<char, string>((char) 8838, "&sube;"),
                      new KeyValuePair<char, string>((char) 8839, "&supe;"),
                      new KeyValuePair<char, string>((char) 8853, "&oplus;"),
                      new KeyValuePair<char, string>((char) 8855, "&otimes;"),
                      new KeyValuePair<char, string>((char) 8869, "&perp;"),
                      new KeyValuePair<char, string>((char) 8901, "&sdot;")
                  };

        private static readonly KeyValuePair<char, string>[] GreekSymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 913, "&Alpha;"),
                      new KeyValuePair<char, string>((char) 914, "&Beta;"),
                      new KeyValuePair<char, string>((char) 915, "&Gamma;"),
                      new KeyValuePair<char, string>((char) 916, "&Delta;"),
                      new KeyValuePair<char, string>((char) 917, "&Epsilon;"),
                      new KeyValuePair<char, string>((char) 918, "&Zeta;"),
                      new KeyValuePair<char, string>((char) 919, "&Eta;"),
                      new KeyValuePair<char, string>((char) 920, "&Theta;"),
                      new KeyValuePair<char, string>((char) 921, "&Iota;"),
                      new KeyValuePair<char, string>((char) 922, "&Kappa;"),
                      new KeyValuePair<char, string>((char) 923, "&Lambda;"),
                      new KeyValuePair<char, string>((char) 924, "&Mu;"),
                      new KeyValuePair<char, string>((char) 925, "&Nu;"),
                      new KeyValuePair<char, string>((char) 926, "&Xi;"),
                      new KeyValuePair<char, string>((char) 927, "&Omicron;"),
                      new KeyValuePair<char, string>((char) 928, "&Pi;"),
                      new KeyValuePair<char, string>((char) 929, "&Rho;"),
                      new KeyValuePair<char, string>((char) 930, "&Sigmaf;"), // undefined (?)
                      new KeyValuePair<char, string>((char) 931, "&Sigma;"),
                      new KeyValuePair<char, string>((char) 932, "&Tau;"),
                      new KeyValuePair<char, string>((char) 933, "&Upsilon;"),
                      new KeyValuePair<char, string>((char) 934, "&Phi;"),
                      new KeyValuePair<char, string>((char) 935, "&Chi;"),
                      new KeyValuePair<char, string>((char) 936, "&Psi;"),
                      new KeyValuePair<char, string>((char) 937, "&Omega;"),
                      new KeyValuePair<char, string>((char) 945, "&alpha;"),
                      new KeyValuePair<char, string>((char) 946, "&beta;"),
                      new KeyValuePair<char, string>((char) 947, "&gamma;"),
                      new KeyValuePair<char, string>((char) 948, "&delta;"),
                      new KeyValuePair<char, string>((char) 949, "&epsilon;"),
                      new KeyValuePair<char, string>((char) 950, "&zeta;"),
                      new KeyValuePair<char, string>((char) 951, "&eta;"),
                      new KeyValuePair<char, string>((char) 952, "&theta;"),
                      new KeyValuePair<char, string>((char) 953, "&iota;"),
                      new KeyValuePair<char, string>((char) 954, "&kappa;"),
                      new KeyValuePair<char, string>((char) 955, "&lambda;"),
                      new KeyValuePair<char, string>((char) 956, "&mu;"),
                      new KeyValuePair<char, string>((char) 957, "&nu;"),
                      new KeyValuePair<char, string>((char) 958, "&xi;"),
                      new KeyValuePair<char, string>((char) 959, "&omicron;"),
                      new KeyValuePair<char, string>((char) 960, "&pi;"),
                      new KeyValuePair<char, string>((char) 961, "&rho;"),
                      new KeyValuePair<char, string>((char) 962, "&sigmaf;"),
                      new KeyValuePair<char, string>((char) 963, "&sigma;"),
                      new KeyValuePair<char, string>((char) 964, "&tau;"),
                      new KeyValuePair<char, string>((char) 965, "&upsilon;"),
                      new KeyValuePair<char, string>((char) 966, "&phi;"),
                      new KeyValuePair<char, string>((char) 967, "&chi;"),
                      new KeyValuePair<char, string>((char) 968, "&psi;"),
                      new KeyValuePair<char, string>((char) 969, "&omega;"),
                      new KeyValuePair<char, string>((char) 977, "&thetasym;"),
                      new KeyValuePair<char, string>((char) 978, "&upsih;"),
                      new KeyValuePair<char, string>((char) 982, "&piv;")
                  };

        private static readonly KeyValuePair<char, string>[] OtherSymbolsReplacementMap
            = new[]
                  {
                      new KeyValuePair<char, string>((char) 338, "&OElig;"),
                      new KeyValuePair<char, string>((char) 339, "&oelig;"),
                      new KeyValuePair<char, string>((char) 352, "&Scaron;"),
                      new KeyValuePair<char, string>((char) 353, "&scaron;"),
                      new KeyValuePair<char, string>((char) 376, "&Yuml;"),
                      new KeyValuePair<char, string>((char) 402, "&fnof;"),
                      new KeyValuePair<char, string>((char) 710, "&circ;"),
                      new KeyValuePair<char, string>((char) 732, "&tilde;"),
                      new KeyValuePair<char, string>((char) 8194, "&ensp;"),
                      new KeyValuePair<char, string>((char) 8195, "&emsp;"),
                      new KeyValuePair<char, string>((char) 8201, "&thinsp;"),
                      new KeyValuePair<char, string>((char) 8204, "&zwnj;"),
                      new KeyValuePair<char, string>((char) 8205, "&zwj;"),
                      new KeyValuePair<char, string>((char) 8206, "&lrm;"),
                      new KeyValuePair<char, string>((char) 8207, "&rlm;"),
                      new KeyValuePair<char, string>((char) 8211, "&ndash;"),
                      new KeyValuePair<char, string>((char) 8212, "&mdash;"),
                      new KeyValuePair<char, string>((char) 8216, "&lsquo;"),
                      new KeyValuePair<char, string>((char) 8217, "&rsquo;"),
                      new KeyValuePair<char, string>((char) 8218, "&sbquo;;"),
                      new KeyValuePair<char, string>((char) 8220, "&ldquo;"),
                      new KeyValuePair<char, string>((char) 8221, "&rdquo;"),
                      new KeyValuePair<char, string>((char) 8222, "&bdquo;"),
                      new KeyValuePair<char, string>((char) 8224, "&dagger;"),
                      new KeyValuePair<char, string>((char) 8225, "&Dagger;"),
                      new KeyValuePair<char, string>((char) 8226, "&bull;"),
                      new KeyValuePair<char, string>((char) 8230, "&hellip;"),
                      new KeyValuePair<char, string>((char) 8240, "&permil;"),
                      new KeyValuePair<char, string>((char) 8242, "&prime;"),
                      new KeyValuePair<char, string>((char) 8243, "&Prime;"),
                      new KeyValuePair<char, string>((char) 8249, "&lsaquo;"),
                      new KeyValuePair<char, string>((char) 8250, "&rsaquo;"),
                      new KeyValuePair<char, string>((char) 8254, "&oline;"),
                      new KeyValuePair<char, string>((char) 8364, "&euro;"),
                      new KeyValuePair<char, string>((char) 8482, "&trade;"),
                      new KeyValuePair<char, string>((char) 8592, "&larr;"),
                      new KeyValuePair<char, string>((char) 8593, "&uarr;"),
                      new KeyValuePair<char, string>((char) 8594, "&rarr;"),
                      new KeyValuePair<char, string>((char) 8595, "&darr;"),
                      new KeyValuePair<char, string>((char) 8596, "&harr;"),
                      new KeyValuePair<char, string>((char) 8629, "&crarr;"),
                      new KeyValuePair<char, string>((char) 8968, "&lceil;"),
                      new KeyValuePair<char, string>((char) 8969, "&rceil;"),
                      new KeyValuePair<char, string>((char) 8970, "&lfloor;"),
                      new KeyValuePair<char, string>((char) 8971, "&rfloor;"),
                      new KeyValuePair<char, string>((char) 9674, "&loz;"),
                      new KeyValuePair<char, string>((char) 9824, "&spades;"),
                      new KeyValuePair<char, string>((char) 9827, "&clubs;"),
                      new KeyValuePair<char, string>((char) 9829, "&hearts;"),
                      new KeyValuePair<char, string>((char) 9830, "&diams;")
                  };

        #endregion

        /// <summary>
        /// Decodes xHTML string with replacing HTML symbols &amp; characters to Windows Phone 7 understandable <see cref="char">chars</see>.
        /// </summary>
        /// <param name="xhtml">xHTML document string.</param>
        /// <param name="includeSpecialCharacters">Flag: indicates that special characters also should be replaced.</param>
        /// <returns>Decoded string.</returns>
        public static string Decode(string xhtml, bool includeSpecialCharacters = false)
        {
            var tmp = xhtml;

            tmp = ReservedSymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            tmp = MathSymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            tmp = GreekSymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            tmp = OtherSymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            tmp = Iso88591SymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            tmp = Iso88591CharactersReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));

            if (includeSpecialCharacters)
            {
                tmp = SpecialReservedSymbolsReplacementMap.Aggregate(tmp, (c, p) => c.Replace(p.Value, string.Concat(p.Key)));
            }

            return tmp;
        }
    }
}